using System;
using HslCommunication.BasicFramework;
using HslCommunication.Core;
using HslCommunication.Serial;

namespace HslCommunication.Profinet.Siemens
{
	/// <summary>
	/// 西门子的PPI协议，适用于s7-200plc，注意，本类库有个致命的风险需要注意，由于本类库的每次通讯分成2次操作，
	/// 故而不支持多线程同时读写，当发生线程竞争的时候，会导致数据异常，想要解决的话，需要您在每次数据交互时添加同步锁。<br />
	/// Siemens's PPI protocol is applicable to s7-200plc. Note that this library has a fatal risk. Please note that because this library is divided into two operations for each communication, 
	/// it does not support simultaneous reading and writing by multiple threads. When thread competition occurs When this happens, 
	/// the data will be abnormal. If you want to solve it, you need to add a synchronization lock at each data interaction.
	/// </summary>
	/// <remarks>
	/// 适用于西门子200的通信，非常感谢 合肥-加劲 的测试，让本类库圆满完成。
	///
	/// 注意：M地址范围有限 0-31地址
	/// </remarks>
	public class SiemensPPI : SerialDeviceBase
	{
		private byte station = 2;

		private byte[] executeConfirm = new byte[6]
		{
			16,
			2,
			0,
			92,
			94,
			22
		};

		/// <summary>
		/// 西门子PLC的站号信息<br />
		/// Siemens PLC station number information
		/// </summary>
		public byte Station
		{
			get
			{
				return station;
			}
			set
			{
				station = value;
				executeConfirm[1] = value;
				int num = 0;
				for (int i = 1; i < 4; i++)
				{
					num += executeConfirm[i];
				}
				executeConfirm[4] = (byte)num;
			}
		}

		/// <summary>
		/// 实例化一个西门子的PPI协议对象<br />
		/// Instantiate a Siemens PPI protocol object
		/// </summary>
		public SiemensPPI()
		{
			base.ByteTransform = new ReverseBytesTransform();
			base.WordLength = 2;
		}

		/// <summary>
		/// 从西门子的PLC中读取数据信息，地址为"M100","AI100","I0","Q0","V100","S100"等<br />
		/// Read data information from Siemens PLC with addresses "M100", "AI100", "I0", "Q0", "V100", "S100", etc.
		/// </summary>
		/// <param name="address">西门子的地址数据信息</param>
		/// <param name="length">数据长度</param>
		/// <returns>带返回结果的结果对象</returns>
		public override OperateResult<byte[]> Read(string address, ushort length)
		{
			OperateResult<byte[]> operateResult = SiemensPPIOverTcp.BuildReadCommand(station, address, length, isBit: false);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			OperateResult<byte[]> operateResult2 = ReadBase(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			if (operateResult2.Content[0] != 229)
			{
				return new OperateResult<byte[]>("PLC Receive Check Failed:" + SoftBasic.ByteToHexString(operateResult2.Content, ' '));
			}
			OperateResult<byte[]> operateResult3 = ReadBase(executeConfirm);
			if (!operateResult3.IsSuccess)
			{
				return operateResult3;
			}
			if (operateResult3.Content.Length < 21)
			{
				return new OperateResult<byte[]>(operateResult3.ErrorCode, "Failed: " + SoftBasic.ByteToHexString(operateResult3.Content, ' '));
			}
			if (operateResult3.Content[17] != 0 || operateResult3.Content[18] != 0)
			{
				return new OperateResult<byte[]>(operateResult3.Content[19], SiemensPPIOverTcp.GetMsgFromStatus(operateResult3.Content[18], operateResult3.Content[19]));
			}
			if (operateResult3.Content[21] != byte.MaxValue)
			{
				return new OperateResult<byte[]>(operateResult3.Content[21], SiemensPPIOverTcp.GetMsgFromStatus(operateResult3.Content[21]));
			}
			byte[] array = new byte[length];
			if (operateResult3.Content[21] == byte.MaxValue && operateResult3.Content[22] == 4)
			{
				Array.Copy(operateResult3.Content, 25, array, 0, length);
			}
			return OperateResult.CreateSuccessResult(array);
		}

		/// <summary>
		/// 从西门子的PLC中读取bool数据信息，地址为"M100.0","AI100.1","I0.3","Q0.6","V100.4","S100"等<br />
		/// Read bool data information from Siemens PLC, the addresses are "M100.0", "AI100.1", "I0.3", "Q0.6", "V100.4", "S100", etc.
		/// </summary>
		/// <param name="address">西门子的地址数据信息</param>
		/// <param name="length">数据长度</param>
		/// <returns>带返回结果的结果对象</returns>
		public override OperateResult<bool[]> ReadBool(string address, ushort length)
		{
			OperateResult<byte[]> operateResult = SiemensPPIOverTcp.BuildReadCommand(station, address, length, isBit: true);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult);
			}
			OperateResult<byte[]> operateResult2 = ReadBase(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult2);
			}
			if (operateResult2.Content[0] != 229)
			{
				return new OperateResult<bool[]>("PLC Receive Check Failed:" + SoftBasic.ByteToHexString(operateResult2.Content, ' '));
			}
			OperateResult<byte[]> operateResult3 = ReadBase(executeConfirm);
			if (!operateResult3.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult3);
			}
			if (operateResult3.Content.Length < 21)
			{
				return new OperateResult<bool[]>(operateResult3.ErrorCode, "Failed: " + SoftBasic.ByteToHexString(operateResult3.Content, ' '));
			}
			if (operateResult3.Content[17] != 0 || operateResult3.Content[18] != 0)
			{
				return new OperateResult<bool[]>(operateResult3.Content[19], SiemensPPIOverTcp.GetMsgFromStatus(operateResult3.Content[18], operateResult3.Content[19]));
			}
			if (operateResult3.Content[21] != byte.MaxValue)
			{
				return new OperateResult<bool[]>(operateResult3.Content[21], SiemensPPIOverTcp.GetMsgFromStatus(operateResult3.Content[21]));
			}
			byte[] array = new byte[operateResult3.Content.Length - 27];
			if (operateResult3.Content[21] == byte.MaxValue && operateResult3.Content[22] == 3)
			{
				Array.Copy(operateResult3.Content, 25, array, 0, array.Length);
			}
			return OperateResult.CreateSuccessResult(SoftBasic.ByteToBoolArray(array, length));
		}

		/// <summary>
		/// 将字节数据写入到西门子PLC中，地址为"M100.0","AI100.1","I0.3","Q0.6","V100.4","S100"等<br />
		/// Write byte data to Siemens PLC with addresses "M100.0", "AI100.1", "I0.3", "Q0.6", "V100.4", "S100", etc.
		/// </summary>
		/// <param name="address">西门子的地址数据信息</param>
		/// <param name="value">数据长度</param>
		/// <returns>带返回结果的结果对象</returns>
		public override OperateResult Write(string address, byte[] value)
		{
			OperateResult<byte[]> operateResult = SiemensPPIOverTcp.BuildWriteCommand(station, address, value);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			OperateResult<byte[]> operateResult2 = ReadBase(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			if (operateResult2.Content[0] != 229)
			{
				return new OperateResult<byte[]>("PLC Receive Check Failed:" + operateResult2.Content[0]);
			}
			OperateResult<byte[]> operateResult3 = ReadBase(executeConfirm);
			if (!operateResult3.IsSuccess)
			{
				return operateResult3;
			}
			if (operateResult3.Content.Length < 21)
			{
				return new OperateResult(operateResult3.ErrorCode, "Failed: " + SoftBasic.ByteToHexString(operateResult3.Content, ' '));
			}
			if (operateResult3.Content[17] != 0 || operateResult3.Content[18] != 0)
			{
				return new OperateResult(operateResult3.Content[19], SiemensPPIOverTcp.GetMsgFromStatus(operateResult3.Content[18], operateResult3.Content[19]));
			}
			if (operateResult3.Content[21] != byte.MaxValue)
			{
				return new OperateResult(operateResult3.Content[21], SiemensPPIOverTcp.GetMsgFromStatus(operateResult3.Content[21]));
			}
			return OperateResult.CreateSuccessResult();
		}

		/// <summary>
		/// 将bool数据写入到西门子PLC中，地址为"M100.0","AI100.1","I0.3","Q0.6","V100.4","S100"等<br />
		/// Write the bool data to Siemens PLC with the addresses "M100.0", "AI100.1", "I0.3", "Q0.6", "V100.4", "S100", etc.
		/// </summary>
		/// <param name="address">西门子的地址数据信息</param>
		/// <param name="value">数据长度</param>
		/// <returns>带返回结果的结果对象</returns>
		public override OperateResult Write(string address, bool[] value)
		{
			OperateResult<byte[]> operateResult = SiemensPPIOverTcp.BuildWriteCommand(station, address, value);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			OperateResult<byte[]> operateResult2 = ReadBase(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			if (operateResult2.Content[0] != 229)
			{
				return new OperateResult<byte[]>("PLC Receive Check Failed:" + operateResult2.Content[0]);
			}
			OperateResult<byte[]> operateResult3 = ReadBase(executeConfirm);
			if (!operateResult3.IsSuccess)
			{
				return operateResult3;
			}
			if (operateResult3.Content.Length < 21)
			{
				return new OperateResult(operateResult3.ErrorCode, "Failed: " + SoftBasic.ByteToHexString(operateResult3.Content, ' '));
			}
			if (operateResult3.Content[17] != 0 || operateResult3.Content[18] != 0)
			{
				return new OperateResult(operateResult3.Content[19], SiemensPPIOverTcp.GetMsgFromStatus(operateResult3.Content[18], operateResult3.Content[19]));
			}
			if (operateResult3.Content[21] != byte.MaxValue)
			{
				return new OperateResult(operateResult3.Content[21], SiemensPPIOverTcp.GetMsgFromStatus(operateResult3.Content[21]));
			}
			return OperateResult.CreateSuccessResult();
		}

		/// <summary>
		/// 从西门子的PLC中读取byte数据信息，地址为"M100","AI100","I0","Q0","V100","S100"等，详细请参照API文档<br />
		/// Read byte data information from Siemens PLC. The addresses are "M100", "AI100", "I0", "Q0", "V100", "S100", etc. Please refer to the API documentation for details.
		/// </summary>
		/// <param name="address">西门子的地址数据信息</param>
		/// <returns>带返回结果的结果对象</returns>
		public OperateResult<byte> ReadByte(string address)
		{
			return ByteTransformHelper.GetResultFromArray(Read(address, 1));
		}

		/// <summary>
		/// 向西门子的PLC中读取byte数据，地址为"M100","AI100","I0","Q0","V100","S100"等，详细请参照API文档<br />
		/// Read byte data from Siemens PLC with addresses "M100", "AI100", "I0", "Q0", "V100", "S100", etc. For details, please refer to the API documentation
		/// </summary>
		/// <param name="address">西门子的地址数据信息</param>
		/// <param name="value">数据长度</param>
		/// <returns>带返回结果的结果对象</returns>
		public OperateResult Write(string address, byte value)
		{
			return Write(address, new byte[1]
			{
				value
			});
		}

		/// <summary>
		/// 启动西门子PLC为RUN模式<br />
		/// Start Siemens PLC in RUN mode
		/// </summary>
		/// <returns>是否启动成功</returns>
		public OperateResult Start()
		{
			byte[] obj = new byte[39]
			{
				104,
				33,
				33,
				104,
				0,
				0,
				108,
				50,
				1,
				0,
				0,
				0,
				0,
				0,
				20,
				0,
				0,
				40,
				0,
				0,
				0,
				0,
				0,
				0,
				253,
				0,
				0,
				9,
				80,
				95,
				80,
				82,
				79,
				71,
				82,
				65,
				77,
				170,
				22
			};
			obj[4] = station;
			byte[] send = obj;
			OperateResult<byte[]> operateResult = ReadBase(send);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			if (operateResult.Content[0] != 229)
			{
				return new OperateResult<byte[]>("PLC Receive Check Failed:" + operateResult.Content[0]);
			}
			OperateResult<byte[]> operateResult2 = ReadBase(executeConfirm);
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			return OperateResult.CreateSuccessResult();
		}

		/// <summary>
		/// 停止西门子PLC，切换为Stop模式<br />
		/// Stop Siemens PLC and switch to Stop mode
		/// </summary>
		/// <returns>是否停止成功</returns>
		public OperateResult Stop()
		{
			byte[] obj = new byte[35]
			{
				104,
				29,
				29,
				104,
				0,
				0,
				108,
				50,
				1,
				0,
				0,
				0,
				0,
				0,
				16,
				0,
				0,
				41,
				0,
				0,
				0,
				0,
				0,
				9,
				80,
				95,
				80,
				82,
				79,
				71,
				82,
				65,
				77,
				170,
				22
			};
			obj[4] = station;
			byte[] send = obj;
			OperateResult<byte[]> operateResult = ReadBase(send);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			if (operateResult.Content[0] != 229)
			{
				return new OperateResult<byte[]>("PLC Receive Check Failed:" + operateResult.Content[0]);
			}
			OperateResult<byte[]> operateResult2 = ReadBase(executeConfirm);
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			return OperateResult.CreateSuccessResult();
		}

		/// <inheritdoc />
		public override string ToString()
		{
			return $"SiemensPPI[{base.PortName}:{base.BaudRate}]";
		}
	}
}
